// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import gatherData from './data/gather';
import globalDictionary from './globalStore/dictionary';
import { findMatchedDictItems } from './data/find';
import { WordsType, DictArray } from './typing';
import { updateConfig, configInfo } from './config';
import { watchConfigFileModification, stopWatchConfigFileModification } from './watch';
import { diagnosticActiveTextEditor } from './diagnostic';
import { registerCodeActionsProvider } from './codeActionsProvider';
import { testingPerformance } from './performance';

const ProjectName = 'Project Dictionary';

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
  // 更新配置
  updateConfig();

  // 获取并保存词典数据
  getAndSaveDictionaryData(configInfo.config.dict);

  // 监听配置文件
  watchConfigFileModification(configInfo.filePath, refreshConfigAndData);

  // 检测代码异常
  diagnosticActiveTextEditor();

  // 注册命令
  registerCommands(context);

  // 监听事件
  listenerEvents();

  // 注册 CodeActionProvider
  registerCodeActionsProvider(context);

  // 测量性能
  // testingPerformance();
}

// this method is called when your extension is deactivated
export function deactivate() {
  stopWatchConfigFileModification();
}

function registerCommands(context: vscode.ExtensionContext) {
	const findDictionaryByWordsDisposable = vscode.commands.registerCommand('projectDictionary.findDictionaryByWords', () => findDictionaryByType('words'));
	const findDictionaryByTranslatedWordsDisposable = vscode.commands.registerCommand('projectDictionary.findDictionaryByTranslatedWords', () => findDictionaryByType('translatedWords'));
	const findDictionaryBySynonymsDisposable = vscode.commands.registerCommand('projectDictionary.findDictionaryBySynonyms', () => findDictionaryByType('synonyms'));
	const findAllDictionaryDisposable = vscode.commands.registerCommand('projectDictionary.findAllDictionary', findAllDictionary);
	const refreshConfigDisposable = vscode.commands.registerCommand('projectDictionary.refreshConfig', refreshConfigAndData);
	const replaceSynonymDisposable = vscode.commands.registerCommand('projectDictionary.replaceSynonym', replaceSynonym);

	context.subscriptions.push(findDictionaryByWordsDisposable);
	context.subscriptions.push(findDictionaryByTranslatedWordsDisposable);
	context.subscriptions.push(findDictionaryBySynonymsDisposable);
	context.subscriptions.push(findAllDictionaryDisposable);
  context.subscriptions.push(refreshConfigDisposable);
  context.subscriptions.push(replaceSynonymDisposable);
}

function listenerEvents() {
  vscode.window.onDidChangeActiveTextEditor(handleChangeActiveTextEditor);
  vscode.workspace.onDidChangeTextDocument(handleChangeTextDocument);
}

function refreshConfigAndData() {
  updateConfig();
  getAndSaveDictionaryData(configInfo.config.dict);
  vscode.window.showInformationMessage(`${ProjectName} 配置更新成功`);
}

function replaceSynonym(document: vscode.TextDocument, range: vscode.Range, unifiedWord: string) {
  const workspaceEdit = new vscode.WorkspaceEdit();
  workspaceEdit.replace(document.uri, range, unifiedWord);

  vscode.workspace.applyEdit(workspaceEdit);
}

function getAndSaveDictionaryData(dictArray: DictArray) {
	const {
    dictWordMap,
    dictTranslatedWordMap,
    dictSynonymMap,
    words,
    translatedWords,
    synonyms,
    allWords,
    synonymsRegExp,
  } = gatherData(dictArray);

  globalDictionary.dictArray = dictArray;
  globalDictionary.dictWordMap = dictWordMap;
  globalDictionary.dictTranslatedWordMap = dictTranslatedWordMap;
  globalDictionary.dictSynonymMap = dictSynonymMap;
  globalDictionary.words = words;
  globalDictionary.translatedWords = translatedWords;
  globalDictionary.synonyms = synonyms;
  globalDictionary.allWords = allWords;
  globalDictionary.synonymsRegExp = synonymsRegExp;


  return globalDictionary;
}

async function findDictionaryByType(type: WordsType) {
  const selectedText = getSelectedText();
  const pickedWord = await showQuickPickByOptions([...globalDictionary.allWords], { value: selectedText });
  if (!pickedWord) {return;}
  
  showMatchedDictItem(type, pickedWord);
}

async function findAllDictionary() {
  const selectedText = getSelectedText();
  const pickedWord = await showQuickPickByOptions([...globalDictionary.allWords], { value: selectedText });
  if (!pickedWord) {return;}

  showMatchedDictItem('all', pickedWord);
}

async function showMatchedDictItem(type: 'all' | WordsType, word: string) {
  const dictItems = findMatchedDictItems(type, word);
  const translatedWords = dictItems.reduce((prev, dictItem) => {
    prev.push(...dictItem['translatedWords']);
    return prev;
  }, [] as string[]);

  const pickedWord = await vscode.window.showQuickPick(translatedWords);

  if (pickedWord) {writeWordToActiveTextEditor(pickedWord);}
}

function writeWordToActiveTextEditor(word: string) {
  if (!vscode.window.activeTextEditor) {return;}

  const { selection } = vscode.window.activeTextEditor;
  const { uri } = vscode.window.activeTextEditor.document;
  const workspaceEdit = new vscode.WorkspaceEdit();
  const range = new vscode.Range(selection.start, selection.end);
  workspaceEdit.replace(uri, range, word);

  vscode.workspace.applyEdit(workspaceEdit);
}

function getSelectedText() {
  if (!vscode.window.activeTextEditor) {return '';}

  const { selection } = vscode.window.activeTextEditor;
  const { getText } = vscode.window.activeTextEditor.document;
  const range = new vscode.Range(selection.start, selection.end);
  const text = getText(range);

  return text;
}

async function showQuickPickByOptions(words: string[], options: { value: string }) {
  const quickPick = vscode.window.createQuickPick();
  quickPick.items = words.map(word => ({ label: word }));
  quickPick.value = options.value;
  quickPick.show();
  
  let pickedText = '';
  quickPick.onDidChangeActive(e => pickedText = e[0].label);
  
  await new Promise(quickPick.onDidAccept);
  quickPick.hide();

  return pickedText;
}

function handleChangeActiveTextEditor(textEditor?: vscode.TextEditor) {
  diagnosticActiveTextEditor();
}

function handleChangeTextDocument(e: vscode.TextDocumentChangeEvent) {
  diagnosticActiveTextEditor();
}
